package drds.server.route;

import java.util.Map;

import drds.server.server.parser.ServerParse;

/**
 * 路由节点
 * 
 * @author 曹正辉<330937205@qq.com>
 */
public final class RouteNode implements Comparable<RouteNode> {
	/** 数据节点名称 */
	private final String id;
	/** 原sql */
	private final String sql;
	/** 在节点上执行的sql */
	private String nodeSql;

	private final int sqlType;
	private volatile boolean writeTrue$ReadFalse;
	private final boolean hasBlanceFlag;
	/** 强制走主从节点 */
	private Boolean masterTrue$SlaveFalse = null; // 默认null表示不施加影响,

	private int limitOffset;
	private int limitSize;
	private int totalNodeSize = 0; // 方便后续jdbc批量获取扩展
	private Route route;

	private String subTableName; // 分表的表名

	public RouteNode(String id, int sqlType, String sql) {
		this.id = id;
		this.limitOffset = 0;
		this.limitSize = -1;
		this.sqlType = sqlType;
		this.sql = sql;
		this.nodeSql = sql;
		this.writeTrue$ReadFalse = (sqlType == ServerParse.SELECT);
		this.hasBlanceFlag = (this.nodeSql != null) && this.nodeSql.startsWith("/*balance*/");
	}

	public Boolean masterTrue$SlaveFalse() {
		return masterTrue$SlaveFalse;
	}

	public void setMasterTrue$SlaveFalse(Boolean masterTrue$SlaveFalse) {
		this.masterTrue$SlaveFalse = masterTrue$SlaveFalse;
	}

	private Map<?, ?> hintMap;

	public Map<?, ?> getHintMap() {
		return hintMap;
	}

	public void setHintMap(Map<?, ?> hintMap) {
		this.hintMap = hintMap;
	}

	public void setNodeSql(String sql) {
		this.nodeSql = sql;
	}

	public void setWriteTrue$ReadFalse(boolean writeTrue$ReadFalse) {
		this.writeTrue$ReadFalse = writeTrue$ReadFalse;
	}

	public boolean getWriteTrue$ReadFalse() {
		return this.writeTrue$ReadFalse;
	}

	public void resetSql() {
		this.nodeSql = sql;
	}

	/**
	 * 这里的逻辑是为了优化，实现：非业务sql可以在负载均衡走slave的效果。因为业务sql一般是非自动提交，
	 * 而非业务sql一般默认是自动提交，比如mysql client，还有SQLJob, heartbeat都可以使用
	 * 了Leader-us优化的query函数，该函数实现为自动提交；
	 * 
	 * 在非自动提交的情况下(有事物)，除非使用了 balance 注解的情况下，才可以走slave.
	 * 
	 * 当然还有一个大前提，必须是 select 或者 show 语句(canRunInReadDB=true)
	 */
	public boolean canRunnOnReadDataBase(boolean autoCommit) {
		return !writeTrue$ReadFalse && //
				(autoCommit //
				|| //
				(!autoCommit && hasBlanceFlag));//
	}

	// public boolean canRunnINReadDB(boolean autocommit) {
	// return canRunInReadDB && autocommit && !hasBlanceFlag
	// || canRunInReadDB && !autocommit && hasBlanceFlag;
	// }

	public String getName() {
		return id;
	}

	public int getSqlType() {
		return sqlType;
	}

	public String getNodeSql() {
		return nodeSql;
	}

	public int getLimitOffset() {
		return limitOffset;
	}

	public void setLimitOffset(int limitOffset) {
		this.limitOffset = limitOffset;
	}

	public int getLimitSize() {
		return limitSize;
	}

	public void setLimitCount(int limitSize) {
		this.limitSize = limitSize;
	}

	public int getTotalNodeSize() {
		return totalNodeSize;
	}

	public void setRouteNodeSize(int totalNodeSize) {
		this.totalNodeSize = totalNodeSize;
	}

	@Override
	public int hashCode() {
		return id.hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o instanceof RouteNode) {
			RouteNode routeNode = (RouteNode) o;
			if (subTableName != null) {
				if (equals(id, routeNode.getName()) && equals(subTableName, routeNode.getSubTableName())) {
					return true;
				}
			} else {
				if (equals(id, routeNode.getName())) {
					return true;
				}
			}
		}
		return false;
	}

	private static boolean equals(String string1, String string2) {
		if (string1 == null) {
			return string2 == null;
		}
		return string1.equals(string2);
	}

	public String getSubTableName() {
		return this.subTableName;
	}

	public void setSubTableName(String subTableName) {
		this.subTableName = subTableName;
	}

	public boolean isModifySQL() {
		return writeTrue$ReadFalse;
	}

	public boolean isDisctTable() {
		if (subTableName != null && !subTableName.equals("")) {
			return true;
		}
		return false;
	}

	@Override
	public int compareTo(RouteNode o) {
		if (o == null) {
			return 1;
		}
		if (this.id == null) {
			return -1;
		}
		if (o.id == null) {
			return 1;
		}
		int c = this.id.compareTo(o.id);
		if (!this.isDisctTable()) {
			return c;
		} else {
			if (c == 0) {
				return this.subTableName.compareTo(o.subTableName);
			}
			return c;
		}
	}

	public boolean hasBlanceFlag() {
		return hasBlanceFlag;
	}

	public Route getRouteContext() {
		return route;
	}

	public void setRoute(Route route) {
		this.route = route;
	}
}
